package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"
)

//首先，我们使用一个标准的sync.Mutex作为Locker来创建Cond。
//接下来，我们创建一个长度为零的切片。 由于我们知道最终会添加10个元素，因此我们将其容量设为10。
//在进入关键的部分前调用Lock来锁定c.L。
//在这里我们检查队列的长度，以确认什么时候需要等待。由于removeFromQueue是异步的，for不满足时才会跳出，而if做不到重复判断，这一点很重要。
//调用Wait，这将阻塞main goroutine，直到接受到信号。
//这里我们创建一个新的goroutine，它会在1秒后将元素移出队列。
//这里我们退出条件的关键部分，因为我们已经成功加入了一个元素。
//我们再次进入该并发条件下的关键部分，以修改与并发条件判断直接相关的数据。
//在这里，我们移除切片的头部并重新分配给第二个元素，这一步模拟了元素出列。
//我们退出操作关键部分，因为我们已经成功移除了一个元素。
//这里，我们发出信号，通知处于等待状态的goroutine可以进行下一步了。
func test1() {
	c := sync.NewCond(&sync.Mutex{})    //1
	queue := make([]interface{}, 0, 10) //2

	removeFromQueue := func(delay time.Duration) {
		time.Sleep(delay)
		c.L.Lock()        //8
		queue = queue[1:] //9
		fmt.Println("Removed from queue", len(queue))
		c.L.Unlock() //10
		c.Signal()   //11
	}

	for i := 0; i < 10; i++ {
		c.L.Lock()            //3
		for len(queue) == 2 { //4
			c.Wait() //5
		}
		fmt.Println("Adding to queue")
		queue = append(queue, struct{}{})
		go removeFromQueue(1 * time.Second) //6
		c.L.Unlock()                        //7
	}
}

type Button struct {
	//1
	Clicked *sync.Cond
}

func test2() {
	button := Button{Clicked: sync.NewCond(&sync.Mutex{})}

	subscribe := func(c *sync.Cond, fn func()) { //2
		var tempwg sync.WaitGroup
		tempwg.Add(1)
		go func() {
			tempwg.Done()
			c.L.Lock()
			defer c.L.Unlock()
			c.Wait()
			fn()
		}()
		tempwg.Wait()
	}

	var wg sync.WaitGroup //3
	wg.Add(3)
	subscribe(button.Clicked, func() { //4
		fmt.Println("Maximizing window.")
		wg.Done()
	})
	subscribe(button.Clicked, func() { //5
		fmt.Println("Displaying annoying dialog box!")
		wg.Done()
	})
	subscribe(button.Clicked, func() { //6
		fmt.Println("Mouse clicked.")
		wg.Done()
	})

	button.Clicked.Broadcast() //7

	wg.Wait()
}

func test3() {
	var onceA, onceB sync.Once
	var initB func()
	initA := func() { onceB.Do(initB) }
	initB = func() { onceA.Do(initA) } // 1
	onceA.Do(initA)                    // 2
}

func test4() {
	myPool := &sync.Pool{
		New: func() interface{} {
			fmt.Println("Creating new instance.")
			return struct{}{}
		},
	}
	myPool.Get()
	myPool.Get()

}

func test5() {
	var or func(channels ...<-chan interface{}) <-chan interface{}

	or = func(channels ...<-chan interface{}) <-chan interface{} { //1
		fmt.Println(22222)
		switch len(channels) {
		case 0: //2
			return nil
		case 1: //3
			return channels[0]
		}

		orDone := make(chan interface{})
		go func() { //4
			defer close(orDone)

			switch len(channels) {
			case 2: //5
				select {
				case <-channels[0]:
				case <-channels[1]:
				}
			default: //6
				select {
				case <-channels[0]:
				case <-channels[1]:
				case <-channels[2]:
				case <-or(append(channels[3:], orDone)...): //6
					fmt.Println(1111)
				}
			}
		}()
		return orDone
	}

	sig := func(after time.Duration) <-chan interface{} { //1
		c := make(chan interface{})
		go func() {
			defer close(c)
			time.Sleep(after)
		}()
		return c
	}

	start := time.Now() //2
	<-or(sig(2*time.Hour), sig(5*time.Minute), sig(10*time.Second), sig(1*time.Hour), sig(1*time.Minute))
	fmt.Printf("done after %v", time.Since(start)) //3

}

func test6() {
	done := make(chan interface{})
	ch := make(chan interface{})
	out1 := make(chan interface{})
	go func() {
		time.Sleep(2 * time.Second)
		ch <- 1
		fmt.Println(<-out1)
	}()
	go func() {
		fmt.Println(<-ch)
	}()
	defer close(done)
	defer close(ch)
	defer close(out1)

	select {

	case out1 <- 2:
	case <-done:
		fmt.Println("done")
	}
	//close(done)
	//close(ch)
}

func main() {
	defer func() {
		time.Sleep(time.Second)
		fmt.Println("the number of goroutines: ", runtime.NumGoroutine())
	}()

	var ch chan int
	go func() {
		<-ch
		// ch<-
	}()
}
